package main

import (
	"fmt"
	"time"
)

func send(ch chan<- int) {
	fmt.Println("before send")
	ch <- 1
	fmt.Println("after send")
}

func receive(ch <-chan int) {
	fmt.Println("before receive")
	<-ch
	fmt.Println("after receive")
}

func main() {
	//default buffer size of channel is 0
	//but you can set buffer size when use make to create channel
	size := 0
    fmt.Println("\n--buffer", size, ", send first--")
	ch := make(chan int, 0)
	//channel buffer size is 0, this means in `send` goroutine execution, when no one tries to receive data before sending data to the channel, the sending to channel will block `send` goroutine and switch to `receive` goroutine. So "after send" is not printed following "before send". But `receive` starts to run and "before receive" is printed, then data is received from channel. After channel data received, the sending block would be released, but with single core CPU, goroutine is not switched immediately when data recevied from channel, so "after receive" is printed and `receive` goroutine ends. Then `send` goroutine resumes to print "after send".
    //but **NOTICE** here, start `send` goroutine before `receive` goroutine NOT means `send` really starts before `receive`, because with single core CPU, the `main` goroutine is running here, `send` and `receive` are both pending, `main` will not block and switch until `time.Sleep`. When switch goroutine from `main`, both the pending `send` and `receive` could be selected as the target running goroutine and left the other still pending. So the explanation before is just a case assuming `send` starts before `receive`. You should run the codes for several times to get the desired running sequence.
	go send(ch)//unlike in javascirpt, this not starts to run immediately
	go receive(ch)//unlike in javascirpt, this not starts to run immediately
    //sleep 2 seconds to make sure the above goroutines end and start new goroutine tests
	time.Sleep(2 * time.Second)

    fmt.Println("\n--buffer", size, ", receive first--")
    //channel buffer size is 0, but try to receive data from channel before sending, so "before receive" is printed, then the `receive` gorouting is blocked and switch to `send` goroutine. So "before send" is printed and because of the data receiving before sending, so sending data to channel here is NOT blocked, with single core CPU this won't switch goroutine, then "after send" is printed and `send` goroutine ends. Then `receive` goroutine resumes to print "after receive". 
    //The same as last example, here assume `receive` starts before `send`
	go receive(ch)//unlike in javascirpt, this not starts to run immediately
	go send(ch)//unlike in javascirpt, this not starts to run immediately
	time.Sleep(2 * time.Second)

    //change buffer size to 1
    size = 1
    fmt.Println("\n--buffer", size, ", send first--")
    ch = make(chan int, size)
	//channel buffer size is 1, this means in `send` gotoutine execution, when no one tries to receive data before sending data to the channel, sending only 1 piece of data to channel will NOT block and switch goroutine, so "after send" is printed immediately following "before send", and `send` goroutine ends. With single core CPU, `receive` goroutine starts to run and "before receive" is printed, then data is received from channel without blocking, then "after receive" is printed.
    //The same as last example, here assume `send` starts before `receive`
	go send(ch)//unlike in javascirpt, this not starts to run immediately
	go receive(ch)//unlike in javascirpt, this not starts to run immediately
	time.Sleep(2 * time.Second)

    fmt.Println("\n--buffer", size, ", receive first--")
    //This is the same as the example that buffer size is 0 and receive first
    //The same as last example, here assume `receive` starts before `send`
	go receive(ch)//unlike in javascirpt, this not starts to run immediately
	go send(ch)//unlike in javascirpt, this not starts to run immediately
	time.Sleep(2 * time.Second)
}
